package com.hrbxlh.doccheck.service.impl;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.ZipUtil;

import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.collect.Lists;
import com.hrbxlh.doccheck.domain.SimilarFile;
import com.hrbxlh.doccheck.service.DocCheckService;
import com.hrbxlh.util.CascaderVO;
import com.hrbxlh.util.DocCheckUtil;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.boot.context.event.ApplicationReadyEvent;

import de.innosystec.unrar.Archive;
import de.innosystec.unrar.NativeStorage;
import de.innosystec.unrar.rarfile.FileHeader;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

import static com.hrbxlh.doccheck.constant.DocCheckConstant.DOC_CHECK_DIR;
/**
 * @author cheer
 */
@Service
public class DocCheckServiceImpl implements DocCheckService, ApplicationListener<ContextRefreshedEvent> {

    @Autowired
    private ThreadPoolTaskExecutor taskExecutor;

    /**
     * 词库是否加载完毕
     */
    private volatile boolean loaded = false;

    private Cache<String, List<SimilarFile>> similarFileCache = CacheBuilder.newBuilder()
            .expireAfterAccess(1, TimeUnit.DAYS)
            .build();


    @Override
    public void uploadFile(String userId, MultipartFile file) throws Exception {
        if (!loaded) {
//            throw new GlobalException("词库加载中，请稍等一下再使用查重工具");
        }

        boolean isZip = StringUtils.endsWith(file.getOriginalFilename(), "zip");
        boolean isRar = StringUtils.endsWith(file.getOriginalFilename(), "rar");
        if (!isZip && !isRar) {
            throw new Exception("文件必须为zip或rar压缩文件");
        }

        String filePath = DOC_CHECK_DIR + userId;
        FileUtil.clean(filePath);
        FileUtil.mkdir(filePath);

        String fileFullName = filePath + File.separator + file.getOriginalFilename();
        try {
            file.transferTo(new File(fileFullName));
        } catch (IOException e) {
            throw new Exception("上传文件异常");
        }

        if (isZip) {
            ZipUtil.unzip(fileFullName, filePath, Charset.forName("gbk"));
        }
        if (isRar) {
            unrar(fileFullName, filePath);
        }
        FileUtil.del(fileFullName);

        similarFileCache.invalidate(String.valueOf(userId));
        List<SimilarFile> similarFiles = compareFileSimilarity(filePath);
        similarFileCache.put(String.valueOf(userId), similarFiles);
    }

    @Override
    public List<CascaderVO> viewFile(String userId) {
        String filePath = DOC_CHECK_DIR + userId;
        FileUtil.mkdir(filePath);
        return listFiles(filePath);
    }

    @Override
    public List<SimilarFile> querySimilarFile(String userId, Double similarity) {
        List<SimilarFile> similarFiles = similarFileCache.getIfPresent(String.valueOf(userId));
        if (CollectionUtils.isEmpty(similarFiles)) {
            return new ArrayList<>();
        }
        List list=Lists.newArrayList();
        for (SimilarFile similarFile2 : similarFiles) {
        	if (similarFile2.getSimilarityScore()>=similarity ) {
        		list.add(similarFile2);
			}
		}
        return list;
    }
    // todo 部署文档需要补充部署rar软件
    private void unrar(String fileFullName, String filePath) {
        String os = System.getProperty("os.name");
        if (os != null && os.toLowerCase().startsWith("win")) {
            createDirectory(fileFullName, filePath);
            unrarFile(fileFullName, filePath);
        } else {
            String cmd = "unrar X -o+ " + fileFullName + " " + filePath;
            try {
                Process proc = Runtime.getRuntime().exec(cmd);
                proc.waitFor();
            } catch (Exception e) {
            }
        }
    }

    /**
     * 创建解压缩需要的文件夹
     */
    private void createDirectory(String fileFullName, String filePath) {
        try (Archive archive = new Archive(new NativeStorage(new File(fileFullName)))) {
            FileHeader fileHeader = archive.nextFileHeader();
            while (fileHeader != null) {
                if (fileHeader.isDirectory()) {
                    String fileName;
                    if (fileHeader.isUnicode()) {
                        fileName = fileHeader.getFileNameW().trim();
                    } else {
                        fileName = fileHeader.getFileNameString().trim();
                    }
                    FileUtil.mkdir(filePath + File.separator + fileName);
                }
                fileHeader = archive.nextFileHeader();
            }
        } catch (Exception e) {
        }
    }
    /**
     * 解压缩文件
     */
    private void unrarFile(String fileFullName, String filePath) {
        try (Archive archive = new Archive(new NativeStorage(new File(fileFullName)))) {
            FileHeader fileHeader = archive.nextFileHeader();
            while (fileHeader != null) {
                if (!fileHeader.isDirectory()) {
                    String fileName;
                    if (fileHeader.isUnicode()) {
                        fileName = fileHeader.getFileNameW().trim().replace("\\", "/");
                    } else {
                        fileName = fileHeader.getFileNameString().replace("\\", "/");
                    }
                    File out = new File(filePath + File.separator + fileName);
                    try (FileOutputStream os = new FileOutputStream(out)) {
                        archive.extractFile(fileHeader, os);
                    } catch (Exception e) {
                    }
                }
                fileHeader = archive.nextFileHeader();
            }
        } catch (Exception e) {
        }
    }

    private List<SimilarFile> compareFileSimilarity(String filePath) {
        List<File> files = new ArrayList<>();
        List<File> allFiles = FileUtil.loopFiles(filePath);
        if (CollectionUtils.isNotEmpty(allFiles)) {
            for (File item : allFiles) {
                boolean isWordFile = StringUtils.endsWith(item.getName(), "doc") ||
                        StringUtils.endsWith(item.getName(), "docx");
                if (item.isFile() && isWordFile) {
                    files.add(item);
                }
            }
        }
        List<SimilarFile> similarFiles = new ArrayList<>();
        String docRootDir = filePath + File.separator;
        if (CollectionUtils.isNotEmpty(files)) {
            for (int i = 0; i < files.size() - 1; i++) {
                for (int j = i + 1; j < files.size(); j++) {
                    String filePathA = files.get(i).getAbsolutePath();
                    String filePathB = files.get(j).getAbsolutePath();
                    SimilarFile similarFile = new SimilarFile();
                    similarFile.setFile(StringUtils.substringAfter(filePathA, docRootDir));
                    similarFile.setComparedFile(StringUtils.substringAfter(filePathB, docRootDir));
                    similarFile.setSimilarityScore(DocCheckUtil.similarity(filePathA, filePathB));
                    similarFiles.add(similarFile);
                }
            }
        }
        return similarFiles;
    }

    private List<CascaderVO> listFiles(String filePath) {
        List<CascaderVO> cascaderVOs = new ArrayList<>();
        File root = new File(filePath);
        File[] allFiles = root.listFiles();
        if (allFiles != null) {
            for (File file : allFiles) {
                CascaderVO cascaderVO =  new CascaderVO();
                cascaderVO.setLabel(file.getName());
                cascaderVO.setChildren(file.isDirectory() ? listFiles(file.getAbsolutePath()) : null);
                cascaderVOs.add(cascaderVO);
            }
        }
        return cascaderVOs;
    }

    @Override
    public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {
        taskExecutor.execute(new Runnable() {
        @Override
        public void run() {
        	DocCheckUtil.similarityString("1", "1");
            try {
                Class.forName("org.apdplat.word.corpus.Bigram");
            } catch (ClassNotFoundException e) {
            }
            loaded = true;
        }
    });
    }
}
